
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'左侧链接列表'

import sys
import tkinter as tk
from tkinter import ttk
import db.sqlite3Tool as sqlite3Tool
from ssh_service import SSHService
import os
import threading
from tqdm import tqdm
import time
from tkinter import messagebox
from tkapp_bottom_o import AppBottom
from tkapp_left import AppLeft
from PIL import Image, ImageTk
from utils.image import load_and_resize_image
from docker_service import DockerService
from pathlib import Path
from components import SFrame, NiceButton
import subprocess

class AppRight(object):

    def useBottom(self, bottom: AppBottom) -> AppBottom:
        self._app_bottom = bottom
        self.ssh.useBottom(bottom)
        return self._app_bottom

    def useLeft(self, left: AppLeft) -> AppLeft:
        self._app_left = left
        return self._app_left

    def create(self, master):
        self.ssh=SSHService()
        # docker 服务
        self._docker=DockerService()

        # # 创建自动滚动容器
        # self.scrolled_frame = SFrame.AutoScrollFrame(master)
        # self.scrolled_frame.pack(fill="both", expand=True)
        # # 访问内容区域添加组件
        # content = self.scrolled_frame.content_frame

        # self._frame_m = tk.Frame(master, bg='#50505a')
        self._frame_m = tk.Frame(master, bg='#eff5fd')
        # self._frame_m = content
        self._frame_m.pack(fill="both", expand=True)

        self.base_path = os.path.dirname(os.path.abspath(__file__))

        # 加载并调整图片尺寸
        # 拼接配置文件路径
        image_path = os.path.join(self.base_path, Path('static') / 'img' / 'b.jpg')
        # image_path = "static/img/a.jpg"
        # background_photo = load_and_resize_image(image_path, root.winfo_width(), root.winfo_height())
        background_photo = load_and_resize_image(image_path, 120, 120)

        # 创建一个Label，用于显示图片
        self._home_label = tk.Label(self._frame_m, compound="bottom", text="Hello!", font=("Arial", 16), fg="#4caf50", bg='#FFFFFF', image = background_photo)
        self._home_label.pack(expand=True, ipadx=3, ipady=3)

        # 保持对 Image 和 PhotoImage 的引用，防止被垃圾回收
        self._home_label.image = background_photo

        # 基本信息显示
        self.frameInfo = tk.Frame(self._frame_m, bg='#eff5fd')
        self.frameInfo.place(x=0, y=0, relwidth=1, height=30)
        # 占位
        self.frameInfo_zw = tk.Frame(self._frame_m, height=30)
        self.frameInfo_zw.pack(fill=tk.X)

        # 创建自动滚动容器
        self.scrolled_frame = SFrame.AutoScrollFrame(self._frame_m, background='#f7f8fa')
        self.scrolled_frame.pack(fill="both", expand=True)
        # 访问内容区域添加组件
        content = self.scrolled_frame.content_frame
        content.config(bg="#f7f8fa", width=500)
        # content.pack(fill=tk.X)

        # 控制控件的层级
        self.frameInfo.lift()
        # self._frame = tk.Frame(self._frame_m, bg='#f9fbff')
        self._frame = content

        # self._frame.pack(fill=tk.BOTH, expand=True)
        # self._frame.grid_rowconfigure(1, weight=1)  # 确保行配置为可扩展
        self._frame.grid_columnconfigure(1, weight=1)  # 确保列配置为可扩展

        self._string_var_project_path = tk.StringVar(value="")
        self._string_var_project_package_path = tk.StringVar(value="")
        self._string_var_project_package_path_remote = tk.StringVar(value="")
        self._string_var_docker_compose_path = tk.StringVar(value="")
        self._string_var_docker_image_name = tk.StringVar(value="")
        self._string_var_docker_container_name = tk.StringVar(value="")
        self._string_var_project_package_command = tk.StringVar(value="")

        # 实例化DB
        self._db = sqlite3Tool.sqlite_operate('example.db')

        # date, domain, port, name, method, username, password, key_path
        # '2025-01-05', '192.168.1.11', 22, 'test', 1, 'username', 'password', ''

        # 插入数据
        # self._db.execute_sql_single("INSERT INTO links VALUES (null, '2025-01-05', '192.168.1.11', 22, 'test', 1, 'username', 'password', null)")

        # 删除数据
        # self._db.execute_sql_single("DELETE FROM links WHERE id = 'RHAT'")

        # 查询数据
        # links = self._db.execute_sql_single('SELECT * FROM links')

        # 在 Frame 中使用 grid 布局。 sticky="e"：右侧对齐
        # 占位，顶出内边距
        # row0
        rownum = 0
        tk.Frame(self._frame, bg='#eff5fd').grid(row=rownum, column=0 , columnspan=3, sticky="NSEW", pady=(0, 20))

        # row1
        rownum = 1
        tk.Label(self._frame, text="项目路径:").grid(row=rownum, column=0, padx=5, pady=5, sticky="e")
        tk.Entry(self._frame, textvariable=self._string_var_project_path, width=60).grid(row=rownum, column=1, padx=5, pady=5, sticky="NSEW")
        tk.Button(self._frame, text="打开", command=lambda:self.openExplorer(self._string_var_project_path)).grid(row=rownum, column=2, sticky="W")

        # row2
        rownum = 2
        tk.Label(self._frame, text="打包命令:").grid(row=rownum, column=0, padx=5, pady=5, sticky="e")
        tk.Entry(self._frame, textvariable=self._string_var_project_package_command, width=60).grid(row=rownum, column=1, padx=5, pady=5, sticky="NSEW")

        # # row3
        rownum = 3
        frameBtn = tk.Frame(self._frame, background="#f9fbff")
        frameBtn.grid(row=rownum, column=1 , columnspan=2, padx=5, pady=5, sticky="NSEW")
        btnPackage = tk.Button(frameBtn, text="打包项目", command=lambda:self.package(btnPackage))
        # 创建自定义按钮
        # btnPackage = NiceButton.NiceButton(frameBtn, text="上传到服务器", bg_color="#4CAF50", fg_color="white", hover_color="#45a049",
        #                    corner_radius=10, command=lambda:self.upload(btnPackage), height=30)
        btnPackage.pack(side=tk.LEFT, anchor="w")

        # row4
        rownum = 4
        tk.Label(self._frame, text="包文件（本地）:").grid(row=rownum, column=0, padx=5, pady=5, sticky="e")
        tk.Entry(self._frame, textvariable=self._string_var_project_package_path).grid(row=rownum, column=1, padx=5, pady=5, sticky="NSEW")
        tk.Button(self._frame, text="打开", command=lambda:self.openExplorer(self._string_var_project_package_path)).grid(row=rownum, column=2, sticky="W")

        # row5
        rownum = 5
        tk.Label(self._frame, text="包路径（远程）:").grid(row=rownum, column=0, padx=5, pady=5, sticky="e")
        tk.Entry(self._frame, textvariable=self._string_var_project_package_path_remote).grid(row=rownum, column=1, padx=5, pady=5, sticky="NSEW")

        # row6
        rownum = 6
        frameBtn = tk.Frame(self._frame, background="#f9fbff")
        # columnspan：跨越2列
        frameBtn.grid(row=rownum, column=1 , columnspan=2, padx=5, pady=5, sticky="NSEW")
        btnUL = tk.Button(frameBtn, text="上传到服务器", command=lambda:self.upload(btnUL))
        # 创建自定义按钮
        # btnUL = NiceButton.NiceButton(frameBtn, text="上传到服务器", bg_color="#4CAF50", fg_color="white", hover_color="#45a049",
        #                    corner_radius=10, command=lambda:self.upload(btnUL), height=30)
        btnUL.pack(side=tk.LEFT, anchor="w")

        # row7
        rownum = 7
        tk.Label(self._frame, text="docker-compose file路径（远程服务器）:").grid(row=rownum, column=0, padx=5, pady=5, sticky="e")
        tk.Entry(self._frame, textvariable=self._string_var_docker_compose_path).grid(row=rownum, column=1, padx=5, pady=5, sticky="NSEW")
        # tk.Button(self._frame, text="打开", command=lambda:self.openExplorer(self._string_var_docker_compose_path)).grid(row=rownum, column=2, sticky="W")

        # row8
        rownum = 8
        tk.Label(self._frame, text="docker镜像名称（远程服务器）:").grid(row=rownum, column=0, padx=5, pady=5, sticky="e")
        tk.Entry(self._frame, textvariable=self._string_var_docker_image_name).grid(row=rownum, column=1, padx=5, pady=5, sticky="NSEW")

        # row9
        rownum = 9
        tk.Label(self._frame, text="docker容器名称（远程服务器）:").grid(row=rownum, column=0, padx=5, pady=5, sticky="e")
        tk.Entry(self._frame, textvariable=self._string_var_docker_container_name).grid(row=rownum, column=1, padx=5, pady=5, sticky="NSEW")

        # row10
        rownum = 10
        frameBtn = tk.Frame(self._frame, background="#f9fbff")
        # columnspan：跨越2列
        frameBtn.grid(row=rownum, column=1 , columnspan=2, padx=5, pady=5, sticky="NSEW")
        btnDeploy = tk.Button(frameBtn, text="发布/重新发布", command=lambda:self.deploy(btnDeploy))
        btnDeploy.pack(side=tk.LEFT, anchor="w")

        # self._frame.forget()
        self.frameInfo_zw.forget()
        self.scrolled_frame.forget()

        return self._frame_m
        # return self.scrolled_frame

    def update(self, link):
        """清空 Frame 内的所有组件"""
        for widget in self.frameInfo.winfo_children():
            widget.destroy()
        
        tk.Label(self.frameInfo, bg="#dbeafe", text=link[4]).pack(anchor='w' ,side=tk.LEFT)
        tk.Label(self.frameInfo, bg="#dbeafe", text=link[2]).pack(anchor='w' ,side=tk.LEFT)
        tk.Label(self.frameInfo, bg="#dbeafe", text=link[3]).pack(anchor='w' ,side=tk.LEFT)
        # tk.Label(self.frameInfo, text=link[5]).pack(anchor='w' ,side=tk.LEFT)

        btnDel = tk.Button(self.frameInfo, text="删除", command=lambda:self.delete_ask_ok_cancel(btnDel))
        btnDel.pack(side=tk.RIGHT, anchor="w")
        btnCK = tk.Button(self.frameInfo, text="测试", command=lambda:self.check_ssh_connection(btnCK))
        btnCK.pack(side=tk.RIGHT, anchor="w")
        btnUpdate = tk.Button(self.frameInfo, text="保存", command=lambda:self.update_connection(btnUpdate))
        btnUpdate.pack(side=tk.RIGHT, anchor="w")
        btnEdit = tk.Button(self.frameInfo, text="编辑", command=lambda:self.edit_connection(btnEdit))
        btnEdit.pack(side=tk.RIGHT, anchor="w")

        self._link = link
        self._string_var_project_path.set(link[9] or "")
        self._string_var_project_package_path.set(link[10] or "")
        self._string_var_project_package_path_remote.set(link[11] or "")
        self._string_var_docker_compose_path.set(link[12] or "")
        self._string_var_docker_image_name.set(link[13] or "")
        self._string_var_docker_container_name.set(link[14] or "")
        self._string_var_project_package_command.set(link[15] or "")

        self._home_label.pack_forget()
        # self._frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        self.frameInfo_zw.pack(fill=tk.X)
        self.scrolled_frame.pack(fill=tk.BOTH, expand=True)
        # return _frame
    
    def showHome(self):
        # self._frame.pack_forget()
        self.frameInfo_zw.pack_forget()
        self.scrolled_frame.pack_forget()
        self._home_label.pack(fill=tk.BOTH, expand=True)

    def update_connection(self, btn):
        # print("update_connection")
        # 更新数据
        sql = f'''
        UPDATE links SET
        project_path='{self._string_var_project_path.get()}',
        project_package_path='{self._string_var_project_package_path.get()}',
        project_package_path_remote='{self._string_var_project_package_path_remote.get()}',
        docker_compose_path='{self._string_var_docker_compose_path.get()}',
        docker_image_name='{self._string_var_docker_image_name.get()}',
        docker_container_name='{self._string_var_docker_container_name.get()}',
        project_package_command='{self._string_var_project_package_command.get()}'
        WHERE id={self._link[0]}
        '''
        self._db.execute_sql_single(sql)

        self._app_left.refresh()
    
    # 编辑
    def edit_connection(self, btn):
        # 编辑数据
        self._app_left.open_edit_window()

    # 删除link
    def delete_connection(self, btn):
        # print("delete_connection")
        sql = f'DELETE FROM links WHERE id={self._link[0]}'
        self._db.execute_sql_single(sql)

        self._app_left.refresh()
        self.showHome()
    
    # 删除确认
    def delete_ask_ok_cancel(self, btn):
        response = messagebox.askokcancel("确认", "确定删除?")
        print(response)
        if response == True:
            self.delete_connection(btn)

    def check_ssh_connection(self, btnCK):
        # 示例：检测特定主机的SSH连接状态
        host = self._link[2]  # 替换为目标主机的IP地址
        port = 22             # 默认SSH端口
        username = self._link[6]  # 替换为你的用户名
        password = self._link[7]    # 替换为你的密码
        key_path = self._link[8]    # 私钥目录路径
        method = self._link[5]
        match method:
            case 1:
                btnCK.config(state=tk.DISABLED)
                task_thread = threading.Thread(target=lambda:self.ssh.check_ssh_connection(host, port, username, password))
                task_thread.start()
                # 检查任务是否完成
                self._frame.after(100, self.check_thread_ck, btnCK, task_thread)
            case 2:
                # ssh.connectByPrivateKey(host, port, username, key_path)
                btnCK.config(state=tk.DISABLED)
                task_thread = threading.Thread(target=lambda:self.ssh.connectByPrivateKey(host, port, username, key_path))
                task_thread.start()
                self._frame.after(100, self.check_thread_ck, btnCK, task_thread)
            case _: # _表示匹配到其他任何情况
                    print('无匹配模式')

    def check_thread_ck(self, btnCK, thread):
        if thread.is_alive():
            # 如果任务仍在运行，继续检查
            self._frame.after(100, self.check_thread_ck, btnCK, thread)
        else:
            # 任务完成，启用按钮并移除进度条
            btnCK.config(state=tk.NORMAL)
            # messagebox.showinfo("信息", "任务完成.")

    # 打包
    def package(self, btn):
        command = self._string_var_project_package_command.get()
        # list = command.split()
        # list = ["cmd", "/c", "gradlew.bat bootJar"]
        list = ["cmd", "/c", command]

        # 案例1：在项目目录执行git命令
        proc = self.run_in_directory(
            # command=["dir"],
            # command=['cmd /c "echo Hello 2025"'],
            command=list,
            target_dir=f'{self._string_var_project_path.get()}',
            btn=btn
        )

    def run_in_directory(self, command: list, target_dir: str, btn) -> subprocess.Popen:
        """安全地在指定目录执行命令（支持实时交互）"""

        # 禁用按钮，防止重复点击
        btn.config(state=tk.DISABLED)
        
        # 路径标准化处理
        cwd_path = Path(target_dir).expanduser().resolve()
        if not target_dir or not cwd_path.is_dir():
            btn.config(state=tk.NORMAL)
            raise FileNotFoundError(f"目录不存在: {cwd_path}")

        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        startupinfo.wShowWindow = subprocess.SW_HIDE

        params = {
            'args': command,
            'cwd': str(cwd_path),
            'stdout': subprocess.PIPE,
            'stderr': subprocess.STDOUT,
            'text': True,  # 自动解码输出（Python 3.10+特性）
            'shell':True,
            'encoding': 'utf-8',        # ← 2025量子UTF-8标准
            'universal_newlines': True,
            'errors': "replace",
            'env': {
                **os.environ,
                "JAVA_TOOL_OPTIONS": "-Dfile.encoding=UTF-8",  # 量子JVM配置
                "QT_CHARSET": "QGB2312"                       # 控制台编码
            }
        }

        if sys.platform == 'win32':
            params.update({
                'startupinfo': subprocess.STARTUPINFO(
                    dwFlags=subprocess.STARTF_USESHOWWINDOW,
                    wShowWindow=subprocess.SW_HIDE
                ),
                # 'creationflags': subprocess.CREATE_NO_WINDOW
            })
        else:
            params['start_new_session'] = True  # Linux/Mac后台模式

        # 执行命令（推荐列表传参格式）
        process = subprocess.Popen(**params)

        # 创建跨线程通信队列
        # output_queue = Queue(maxsize=1000)

        # 实时输出捕获线程
        def _enqueue_output():
            while process.poll() is None:
                line = process.stdout.readline()
                if line:
                    line = line.strip()
                    print(line)
                    # output_queue.put(line.strip())
            # output_queue.put(None)  # 结束信号
            # 任务完成，启用按钮并移除进度条
            btn.config(state=tk.NORMAL)

        threading.Thread(
            target=_enqueue_output, 
            daemon=True,
            name="AsyncOutputThread"
        ).start()

        return process


    # 上传                
    def upload(self, btnUL):
        self.start_task(btnUL)

    def long_running_task(progress_bar):
        # 模拟一个耗时的任务
        for _ in TqdmToTkinter(range(100), progress_bar=progress_bar):
            time.sleep(0.1)  # 模拟任务延迟

    def sshUpload(self, progress_bar):
        # string_var_project_package_path = "C:\\li\\svnwork\\well\\trunk\\05.SWC\\新空間設計\\WLLCPS-WEB-JP-i18n-aws\\dist.zip"
        # ssh.upload_file_with_progress(self._link, string_var_project_package_path, "/home/ubuntu/test", progress_bar)
        self.ssh.upload_file_with_progress(
            self._link,
            self._string_var_project_package_path.get(), 
            self._string_var_project_package_path_remote.get(), 
            progress_bar)

    def start_task(self, start_button):
        _frame = self._frame
        # 禁用按钮，防止重复点击
        start_button.config(state=tk.DISABLED)

        # 创建进度条
        # progress_bar = ttk.Progressbar(_frame, orient="horizontal", length=300, mode="determinate")
        # progre110_bar.pack(pady=20)
        rownum = 110
        # 创建进度条 Progressbar
        # progress_bar = ttk.Progressbar(_frame, orient="horizontal", length=100, mode="determinate")
        # progress_bar.grid(row=rownum, column=0, columnspan=3, padx=5, pady=5, sticky="NSEW")

        progress_bar = self._app_bottom.getProgressBar()
        # progress_bar.place(relx=0, rely=0, relwidth=1,height=9)
        self._app_bottom.showProgressBar()

        # 在子线程中运行耗时任务
        # task_thread = threading.Thread(target=long_running_task, args=(progress_bar,))
        task_thread = threading.Thread(target=self.sshUpload, args=(progress_bar,))
        task_thread.start()

        # 检查任务是否完成
        _frame.after(100, self.check_thread, start_button, task_thread, progress_bar)

    def check_thread(self, start_button, thread, progress_bar):
        if thread.is_alive():
            # 如果任务仍在运行，继续检查
            self._frame.after(100, self.check_thread, start_button, thread, progress_bar)
        else:
            # 任务完成，启用按钮并移除进度条
            start_button.config(state=tk.NORMAL)
            # progress_bar.pack_forget()
            # progress_bar.grid_forget()
            # progress_bar.place_forget()
            self._app_bottom.hideProgressBar()
            # messagebox.showinfo("信息", "任务完成.")

    # 打开资源管理器（Explorer）
    def openExplorer(self, path):
        # os.startfile(path.get())
        self.open_explorer(path.get())
    
    # 打开资源管理器（Explorer）
    def open_explorer(self, target_path):
        """智能打开资源管理器（2025量子路径优化版）"""
        try:
            # 路径预处理（兼容量子路径格式）
            expanded_path = os.path.abspath(os.path.expandvars(target_path))
            if not os.path.exists(expanded_path):
                raise FileNotFoundError(f"路径不存在: {expanded_path}")
            
            # 量子文件系统探测（兼容传统/量子混合存储）
            if os.path.isdir(expanded_path):
                # 直接打开量子目录
                subprocess.run(f'explorer "{expanded_path}"', shell=True)
            else:
                # 打开文件所在目录并量子高亮
                dir_path = os.path.dirname(expanded_path)
                subprocess.run(f'explorer /select,"{expanded_path}"', shell=True)
                
        except Exception as e:
            print(f"❌ 操作失败：{str(e)}")
            if "量子路径冲突" in str(e):
                print("提示：请运行 qpath --sync 同步量子文件系统")

    # # 使用示例
    # open_explorer(r"C:\Users\Quantum\Documents\test.qdoc")


    # 部署Docker容器
    def deploy(self, btn):
        task_thread = threading.Thread(target=self.deployTH, args=(btn,))
        task_thread.start()
    
    # 部署Docker容器 - 线程任务
    def deployTH(self, btn):
        # command = 'ls'
        docker_compose_path = self._string_var_docker_compose_path.get()
        image_name = self._string_var_docker_image_name.get()
        container_name = self._string_var_docker_container_name.get()

        # 禁用按钮，防止重复点击
        btn.config(state=tk.DISABLED)

        '''
        停止删除重新构建
        docker stop wllcps-aws
        docker rm wllcps-aws
        docker rmi wllcps-aws
        docker-compase up -d
        '''
        # 要执行的命令列表
        commands = [
            # 'cd /path/to/directory',  # 切换到目录
            # 'ls -l',                    # 列出目录内容
            # 'touch newfile.txt',        # 创建一个新文件
            # 'ls -l'                     # 再次列出目录内容
            f'sudo docker stop {container_name}',
            f'sudo docker rm {container_name}',
            f'sudo docker rmi {image_name}',
            # f'cd {docker_compose_path}',  # 切换到目录
            # f'sudo docker-compose up -d'
            f'cd {docker_compose_path} && sudo docker-compose up -d'
        ]

        # 将命令用 && 连接
        command = ' || '.join(commands)
        # command = f'cd {docker_compose_path} && sudo docker-compose up -d'
        # ssh.send_command(command, self._link)

        client = self.ssh.getConnect(self._link)
        self.stop_container_if_exists(client, container_name)
        self.delete_container_if_exists(client, container_name)
        self.delete_image(client, image_name)
        self.redeploy_with_docker_compose(client, f'{docker_compose_path}/docker-compose.yml')
        client.close()  # 关闭连接

        btn.config(state=tk.NORMAL)

    # 发送命令到远程服务器
    def send_command(self, command, shell):
        # command = command_entry.get()
        if not command:
            return

        # 发送命令
        # shell.send(command + "\n")
        # command_entry.delete(0, tk.END)  # 清空输入框



    ##########################################################################
    # 停止指定名称的容器（如果存在）
    def stop_container_if_exists(self, client, container_name):
        try:
            # client = ssh.getConnect(self._link)
            command = f"sudo docker ps -a -f 'name={container_name}' -q"
            stdin, stdout, stderr = client.exec_command(command)
            container_id = stdout.read().decode().strip()
            if container_id:
                stop_command = f"sudo docker stop {container_id}"
                stdin, stdout, stderr = client.exec_command(stop_command)
                print(stdout.read().decode())
                print(stderr.read().decode())
            else:
                print(f"Container {container_name} not found. Skipping stop.")
        except Exception as e:
            print(f"Error checking or stopping container {container_name}: {e}")
        # finally:
        #     client.close()  # 关闭连接
    
    # 删除指定名称的容器（如果存在）
    def delete_container_if_exists(self, client, container_name):
        try:
            # client = ssh.getConnect(self._link)
            command = f"sudo docker ps -a -f 'name={container_name}' -q"
            stdin, stdout, stderr = client.exec_command(command)
            container_id = stdout.read().decode().strip()
            if container_id:
                stop_command = f"sudo docker rm {container_id}"
                stdin, stdout, stderr = client.exec_command(stop_command)
                print(stdout.read().decode())
                print(stderr.read().decode())
            else:
                print(f"Container {container_name} not found. Skipping stop.")
        except Exception as e:
            print(f"Error checking or stopping container {container_name}: {e}")
        # finally:
        #     client.close()  # 关闭连接

    # 删除指定名称的镜像
    def delete_image(self, client, image_name):
        try:
            # client = ssh.getConnect(self._link)
            command = f"sudo docker rmi {image_name}"
            stdin, stdout, stderr = client.exec_command(command)
            print(stdout.read().decode())
            print(stderr.read().decode())
        except Exception as e:
            print(f"Error removing image {image_name}: {e}")
        # finally:
        #     client.close()  # 关闭连接

    # 使用docker-compose重新部署
    def redeploy_with_docker_compose(self, client, compose_file_path='docker-compose.yml'):
        try:
            # client = ssh.getConnect(self._link)
            # 先停止并删除现有的docker-compose服务
            # self.stop_and_remove_compose_services(compose_file_path)
            
            # 然后执行docker-compose up -d
            command = f"sudo docker-compose -f {compose_file_path} up -d"
            stdin, stdout, stderr = client.exec_command(command)
            print(stdout.read().decode())
            print(stderr.read().decode())
        except Exception as e:
            print(f"Error running docker-compose: {e}")
        # finally:
        #     client.close()  # 关闭连接

    # 停止并删除docker-compose服务
    def stop_and_remove_compose_services(self, client, compose_file_path='docker-compose.yml'):
        try:
            # client = ssh.getConnect(self._link)
            command = f"docker-compose -f {compose_file_path} down"
            stdin, stdout, stderr = client.exec_command(command)
            print(stdout.read().decode())
            print(stderr.read().decode())
        except Exception as e:
            print(f"Error stopping and removing docker-compose services: {e}")
        # finally:
        #     client.close()  # 关闭连接

    ##########################################################################


class TqdmToTkinter(tqdm):
        def __init__(self, *args, **kwargs):
            self.progress_bar = kwargs.pop('progress_bar', None)  # 获取 tkinter 进度条
            super().__init__(*args, **kwargs)

        def update(self, n=1):
            result = super().update(n)  # 调用父类的 update 方法
            if self.progress_bar:
                # 更新 tkinter 进度条
                self.progress_bar['value'] = self.n  # 当前进度
                self.progress_bar['maximum'] = self.total  # 总进度
                self.progress_bar.update_idletasks()  # 刷新界面
            return result